type jsonInfo = { ip: string, pro?: string, province?: string, city: string, os?: string, browser?: string };
type apiInfo = { url: string, type: string };
/*@ type: jsonp、json、json.data、json.datas */
const urls: apiInfo[] = [
  { url: 'https://whois.pconline.com.cn/ipJson.jsp', type: 'jsonp'},
  { url: 'https://ip.useragentinfo.com/jsonp', type: 'jsonp'},
  { url: 'https://ip.help.bj.cn/', type: 'json.datas'}
]
function createScript(url: string, cbName: string) {
  const script = document.createElement('script');
  script.setAttribute('src', url);
  script.id = cbName;
  document.getElementsByTagName('head')[0].appendChild(script);
  const timer = window.setTimeout(function () {
    removeScipt(cbName);
    removeCb(cbName);
    clearTimeout(timer);
  }, 2000)
}
function removeScipt(id: string) {
  const script = document.getElementById(id);
  document.getElementsByTagName('head')[0].removeChild(script);
}
function getCb() {
  return `jsonp${Math.ceil(Math.random() * 10000)}`;
}
function removeCb(cbName: string) {
  try {
    delete window[cbName];
  } catch (e) {
    window[cbName] = undefined;
  }
}
function useApi(api: apiInfo): Promise<any> {
  return new Promise((resolve) => {
    if (api.type == 'jsonp'){
      const cbName = getCb();
      api.url += `?callback=${cbName}`
      window[cbName] = (res: any) => {
        resolve(res);
      }
      createScript(api.url, cbName);
    } else {
      fetch(api.url)
        .then(res => res.json())
        .then(data => {
          if (api.type == 'json.data'){
            resolve(data.data)
          } else if (api.type == 'json.datas'){
            resolve(data.data[0])
          } else {
            resolve(data)
          }
        })
    }
  })
}
function fetchBrowser() {
  const ua = navigator.userAgent.toLocaleLowerCase();
  if (/micromessenger/.test(ua)) return 'wechat';
  if (/qq/.test(ua)) return 'qqbrowser';
  if (/bidu/.test(ua)) return 'bidu';
  if (/xiaomi/.test(ua)) return 'xiaomi';
  if (/huawei/.test(ua)) return 'huawei';
  if (/metasr/.test(ua) || /sougou/.test(ua)) return 'sougou';
  if (/ucbrowser/.test(ua)) return 'uc';
  if (/maxthon/.test(ua)) return 'maxthon';
  if (/quark/.test(ua)) return 'quark';
  if (/opera/.test(ua)) return 'opera';
  if (/edg/.test(ua)) return 'edge';
  if (/chrome/.test(ua)) return 'chrome';
  if (/safari/.test(ua)) return 'safari';
  if (/msie/.test(ua)) return 'ie';
  if (/firefox/.test(ua)) return 'firefox';
  return 'others';
}
function fetchOs() {
  const ua = navigator.userAgent.toLocaleLowerCase();
  if (/windows nt 5.1/.test(ua) || /windows xp/.test(ua)) return 'WinXP';
  if (/windows nt 6.0/.test(ua) || /windows vista/.test(ua)) return 'WinVista';
  if (/windows nt 6.1/.test(ua) || /windows 7/.test(ua)) return 'Win7';
  if (/windows nt 10.0/.test(ua) || /windows 10/.test(ua)) return 'Win10/11';
  if (/android/.test(ua)) return 'Android';
  if (/linux/.test(ua)) return 'Linux';
  if (/iphone/.test(ua)) return 'iPhone/iOS';
  if (/ipad/.test(ua)) return 'iPad/iOS';
  if (/macintosh/.test(ua)) return 'Mac';
  if (/windows phone/.test(ua)) return 'WinPhone';
  if (/symbianos/.test(ua)) return 'Symbian';
  return 'others'
}
const PromiseAny = function (pros: Promise<any>[]): Promise<any> {
  return new Promise((resolve, reject) => {
    pros.forEach((pro) => {
      pro
        .then(value => { resolve(value) })
        .catch(err => reject(err))
    })
  })
}
//Unable to use 'for、foreach...'
const pros = [
   useApi(urls[0]),
   useApi(urls[1]),
   useApi(urls[2]),
];
export function vtoinfo(): Promise<jsonInfo> {
  return new Promise((resolve) => {
    PromiseAny(pros).then(value => {
      const data = value as jsonInfo;
      const info: jsonInfo = {
        ip: data.ip,
        province: data.province || data.pro,
        city: data.city,
        os: fetchOs(),
        browser: fetchBrowser()
      }
      resolve(info)
    }).catch(err => {
      resolve(err)
      //reject('fetch info failed.');
    })
  });
}